package com.jojo.jojomall.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.jojo.common.to.es.skuEsModel;
import com.jojo.jojomall.search.config.jojomallElasticConfig;
import com.jojo.jojomall.search.constant.EsConstant;
import com.jojo.jojomall.search.service.MallSearchService;
import com.jojo.jojomall.search.vo.SearchParam;
import com.jojo.jojomall.search.vo.SearchResult;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.swing.text.Highlighter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @Author: 魔法少女小夫
 * @Data: 2022/5/17
 * @Version:
 * @Description:
 */
@Service
public class MallSearchServiceImpl implements MallSearchService {
    @Autowired
    RestHighLevelClient client;
    @Override
    public SearchResult search(SearchParam param) {
//        列好返回值,要给客户端返回的结果数据
        SearchResult result = null;
//       准备检索请求，
        SearchRequest searchRequest = new SearchRequest();
//        使用一个方法去封装 返回值就是我们的检索请求；在这个方法中动态构建出检索语句；并以SearchRequest返回
       searchRequest= buidSearchRequest(param);


        SearchResponse response =null;
        try {
//使用elasticConfig构建出客户端去执行
            response = client.search(searchRequest, jojomallElasticConfig.COMMON_OPTIONS);
        } catch (IOException e) {
            e.printStackTrace();
        }

//        将返回结果封装到方法去实现，这里直接调用
       result=buidSearchResult(response,param);

        return result;
    }


    /**
 * @Author 小夫
 * @Description 准备检索请求，过滤（按照属性，分类等），排序分页，高亮，聚合分析；
 * @Date 21:14 2022/5/17
 * @Param []
 * @return org.elasticsearch.action.search.SearchRequest
 *
     * @param param*/
    private SearchRequest buidSearchRequest(SearchParam param) {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();//构建dsl语句载体
        /*准备检索请求，过滤（按照属性，分类等）*/
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        /*1.1,must条件*/
        //首先进行判断
        if (!StringUtils.isEmpty(param.getKeyword())){
            //1.1 must模糊匹配，
        boolQuery.must(QueryBuilders.matchQuery("skuTitle",param.getKeyword()));
        }
        //1.2.1,过滤查询；按照三级分类id来查
        if (param.getCatalog3Id()!=null) {
            boolQuery.filter(QueryBuilders.termQuery("catalogId",param.getCatalog3Id()));
        }
        //1.2.2,过滤查询；按照品牌id来查（是一个数组，用terms来查）
        if (param.getBrandId()!=null&&param.getBrandId().size()>0){
            //
            boolQuery.filter(QueryBuilders.termsQuery("brandId",param.getBrandId()));
        }
//        1.2.3,过滤查询；按照库存是否有来进行查询
            boolQuery.filter(QueryBuilders.termQuery("hasStock",param.getHasStock()==1));
//        1.2.4,过滤查询；按照skuprice价格区间检索： String=>  ($)_($) 价格之间
        if(!StringUtils.isEmpty(param.getSkuPrice())){
            RangeQueryBuilder rangePrice = QueryBuilders.rangeQuery("skuPrice");
            String[] s = param.getSkuPrice().split("_");
            if (s.length==2){
            rangePrice.gte(s[0]).lte(s[1]);
            }else if (s.length==1){
                if (param.getSkuPrice().startsWith("_")){// _$
                    rangePrice.lte(s[0]);
                }
                if (param.getSkuPrice().endsWith("_")){//   $_
                    rangePrice.gte(s[0]);
                }

            }
            boolQuery.filter(rangePrice);
        }
//          1.2.5 过滤查询:按照所有指定的属性进行查询：
        if (param.getAttrs()!=null&&param.getAttrs().size()>0){
//            因为用到了nested，
            for (String attr : param.getAttrs()) {
            BoolQueryBuilder nestedBoolQuery = QueryBuilders.boolQuery();
                String[] s = attr.split("_");
                String attrid=s[0];
                String[] attrsvalues = s[1].split(":");//属性
                nestedBoolQuery.must(QueryBuilders.termQuery("attrs.attrId",s[0]));
                nestedBoolQuery.must(QueryBuilders.termsQuery("attrs.attrValue",attrsvalues));
//                每一个都需要生成一个nested去查询
            NestedQueryBuilder nestedQuery = QueryBuilders.nestedQuery("attrs", nestedBoolQuery, ScoreMode.None);
            //施加过滤
            boolQuery.filter(nestedQuery);
            }
        }
        sourceBuilder.query(boolQuery);/*这里装入了boolQueryBuilder执行bool结果*/  //先装入查询和过滤结果
//        接下来封装
//        排序分页，高亮，聚合分析；
//        1.3 排序
        if (!StringUtils.isEmpty(param.getSort())){
            String sort = param.getSort();
            String[] s = sort.split("_");//s[0]是排序名称（属性），s[1]是排序顺序
          SortOrder sortOrder = s[1].equalsIgnoreCase("asc")?SortOrder.ASC:SortOrder.DESC;
            sourceBuilder.sort(s[0],sortOrder);
        }
//        1.4分页实现
        /*from=(pagenumber-1)*size*/
        sourceBuilder.from((param.getPageNumber()-1)*EsConstant.PRODUCT_PAGESIZE);
        sourceBuilder.size(EsConstant.PRODUCT_PAGESIZE);
//        1.5  高亮实现； 模糊匹配
        if (!StringUtils.isEmpty(param.getKeyword())){
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("skuTitle");
//            设定前置标签：
            highlightBuilder.preTags("<b style ='color:red'>");
            highlightBuilder.postTags("</b>");
            sourceBuilder.highlighter(highlightBuilder);
        }



//2.1.1 聚合分析
        /*品牌聚合*/
        TermsAggregationBuilder brand_agg = AggregationBuilders.terms("brand_agg");
        brand_agg.field("brandId").size(50);//看看有多少种品牌
//        品牌聚合的子聚合：
        /*每种品牌对应的name*/
        brand_agg.subAggregation(AggregationBuilders.terms("brand_name_agg").field("brandName.keyword").size(1));
        /*每种品牌对应的图片儿*/
        brand_agg.subAggregation(AggregationBuilders.terms("brand_img_agg").field("brandImg.keyword").size(1));
        sourceBuilder.aggregation(brand_agg);
//2.1.2 聚合分析
        /*分类聚合*/
        TermsAggregationBuilder catalog_agg = AggregationBuilders.terms("catalog_agg").field("catalogId");
//        分类聚合的子聚合：
        /*每个分类对应的名字*/
        catalog_agg.subAggregation(AggregationBuilders.terms("catalog_name_agg").field("catalogName.keyword").size(1));
        sourceBuilder.aggregation(catalog_agg);
//2.1.2 聚合分析
        /*属性聚合*/
        NestedAggregationBuilder attr_agg = AggregationBuilders.nested("attr_agg", "attrs");
//        属性聚合的子聚合 ，且再nested下；
        TermsAggregationBuilder attr_id_agg = AggregationBuilders.terms("attr_id_agg").field("attrs.attrId.keyword");
//        子聚合中的子聚合 弟中支第
        /*聚合分析当前的attrid对应的名字*/
        attr_id_agg.subAggregation(AggregationBuilders.terms("attr_name_agg").field("attrs.attrName.keyword").size(1));
        /*聚合分析当前的attrid对应的所有可能的属性*/
        attr_id_agg.subAggregation(AggregationBuilders.terms("attr_value").field("attrs.attrValue.keyword").size(50));

        attr_agg.subAggregation(attr_id_agg);
        sourceBuilder.aggregation(attr_agg);
//        准备检索请求，这里执行我们写的sourceBuilder
        System.out.println(sourceBuilder.toString());
        SearchRequest searchRequest = new SearchRequest(new String[]{EsConstant.PRODUCT_INDEX}, sourceBuilder);

        return searchRequest;

    }

    /**
     * @Author 小夫
     * @Description  构建结果数据
     * @Date 21:20 2022/5/17
     * @Param [response]
     * @return com.jojo.jojomall.search.vo.SearchResult  根据检索返回的解锁结果；
     **/
    private SearchResult buidSearchResult(SearchResponse response, SearchParam param) {
        //        构建结果
        SearchResult result = new SearchResult();
//        /*当前商品涉及到的所有属性信息*/
        List<SearchResult.AttrVo> attrVos =new ArrayList<>();
        ParsedNested attr_agg = response.getAggregations().get("attr_agg");
//        拿到下面的子聚合|
        ParsedLongTerms attr_agg_id = attr_agg.getAggregations().get("attr_agg_id");
        for (Terms.Bucket bucket : attr_agg_id.getBuckets()) {
//            每一个都需要一个对象来封装然后撞到集合里
            SearchResult.AttrVo attrVo = new SearchResult.AttrVo();
            /*得到属性的id*/
            Long attrid = bucket.getKeyAsNumber().longValue();
//            得到属性的名字
            ParsedStringTerms attr_name_agg = bucket.getAggregations().get("attr_name_agg");
            String attrname = attr_name_agg.getBuckets().get(0).getKeyAsString();
            /*得到属性的所有值*/
            ParsedStringTerms attr_value_agg = bucket.getAggregations().get("attr_value_agg");
            List<String> valueList = attr_value_agg.getBuckets().stream().map(item -> {
                String keyAsString = item.getKeyAsString();
                return keyAsString;
            }).collect(Collectors.toList());
            attrVo.setAttrId(attrid);
            attrVo.setAttrName(attrname);
            attrVo.setAttrValue(valueList);
            attrVos.add(attrVo);
        }

//        result.setAttrs();
//        /*当前商品所涉及到的所有品牌信息*/
//        result.setBrands();
//        /*当前商品所涉及到的分类信息*/
        ParsedLongTerms catalog_agg = response.getAggregations().get("catalog_agg");
        List<SearchResult.CatalogVo> catalogVos=new ArrayList<>();
        for (Terms.Bucket bucket : catalog_agg.getBuckets()) {
            SearchResult.CatalogVo catalogVo = new SearchResult.CatalogVo();
            String keyAsString = bucket.getKeyAsString();
//            拿到id后转换并设置
            catalogVo.setCatalogId(Long.parseLong(keyAsString));
//            得到分类名
            ParsedStringTerms catalog_name_agg = bucket.getAggregations().get("catalog_name_agg");
            String nameinfo = catalog_name_agg.getBuckets().get(0).getKeyAsString();
            catalogVo.setCatalogName(nameinfo);
            catalogVos.add(catalogVo);
        }

//        result.setCatalogs();
////     ==========以上从聚合信息中获取===========
//        分页信息，页码 :当前页
        result.setPageNum(param.getPageNumber());
//        分页信息，总记录数
        SearchHits hits = response.getHits();
        long totalvalue = hits.getTotalHits().value;//总记录数
        result.setTotal(totalvalue);//将总记录数存进来
        /*分页信息->总页码 -计算-》 11、2=5.。1*/
        Integer totalpages =totalvalue%EsConstant.PRODUCT_PAGESIZE==0?(int)totalvalue/EsConstant.PRODUCT_PAGESIZE:((int)totalvalue/EsConstant.PRODUCT_PAGESIZE+1);
        result.setTotalPages(totalpages);
        /*创建一个集合来收集每个命中的商品信息*/
        List<skuEsModel> esModels=new ArrayList<>();
if (Objects.nonNull(hits.getHits())&&hits.getHits().length>0){
    for (SearchHit hit : hits.getHits()) {
        String sourceAsString = hit.getSourceAsString();
//        把命中的商品信息转回以前的对象
       skuEsModel esModel=  JSON.parseObject(sourceAsString,skuEsModel.class);
//       将每一个商品的信息保存起来
       esModels.add(esModel);
    }
}
//将商品信息封装好；
result.setProducts(esModels);

//          获取catalog_agg聚合




        return result ;
    }



}
